/*****************************************************************************
Copyright 2006 Kervin L. Pierre
    EMAIL: otlkcon@adevsoft.com

This file is part of Otlkcon.

Otlkcon is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.

Otlkcon is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Lesser GNU General Public License for more details.

You should have received a copy of the Lesser GNU General Public License
along with Otlkcon; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

*******************************************************************************/

#include "StdAfx.h"
#include "O_IMsgStore.h"
#include "O_IMSProvider.h"
#include "otlkcon_account.h"
#include "mstore.h"

#include <mspst.h>

extern HWND  otlkcon_eventHwnd;

O_IMSPMsgStore::O_IMSPMsgStore()
{
    className = _strdup("O_IMSPMsgStore");
}

HRESULT O_IMSPMsgStore::Create(char * df, MAPIUID ig, otlkcon_memory *m )
{
    HRESULT result = S_OK;
    O_IFolder *rootFolder;
    O_IFolder *IPMroot;
    O_ENTRYID leid = {0};
    ULONG propCount = 0, tmpPropTag;
    SPropValue defaultProps[30] = {0};
    MAPINAMEID tmpNameID = {0};
    LPMAPINAMEID tmpNameIDs = &tmpNameID;

    LPSPropTagArray recvFldPropTag;
    MAPINAMEID recvFld = {0};
    LPMAPINAMEID recvFlds = &recvFld;
    LPSPropTagArray tmpPropTagArray = NULL;

    OTLKCON_ENTERCRITICALSECTION(&otlkcon_sqlite_cs, "SQL> otlkcon_generic_msgstore::Create");
    OTLKCON_ENTERCRITICALSECTION(&objectCS, "OBJ> otlkcon_generic_msgstore::Create");

    result = otlkcon_generic_msgstore::Create(df,ig,m);
    if( result != S_OK )
    {
        OTLKCON_DEBUGBREAK;
        return result;
    }

    entryID->primarySupportedInterface = OTLKCON_IMSPMSGSTORE_CLASS;

    rootFolder = topLevelFolders["ROOT_FOLDER"];

    //PR_ENTRYID
    defaultProps[propCount].ulPropTag  = PR_ENTRYID;
    defaultProps[propCount].Value.bin.lpb = (LPBYTE)entryID;
	defaultProps[propCount++].Value.bin.cb  = sizeof(O_ENTRYID);

	//Get the PR_IPM_SUBTREE_ENTRYID
    result = rootFolder->CreateFolder(FOLDER_GENERIC,"Top of Personal Folders",
            "",NULL,OPEN_IF_EXISTS,(LPMAPIFOLDER *)&IPMroot);
    if( result != S_OK )
    {
        OTLKCON_DEBUGBREAK;
    }

	defaultProps[propCount].ulPropTag       = PR_IPM_SUBTREE_ENTRYID;
	defaultProps[propCount].Value.bin.cb    = sizeof(O_ENTRYID);
    defaultProps[propCount++].Value.bin.lpb = (LPBYTE)IPMroot->entryID;

    topLevelFolders["IPM"] = IPMroot;

    // Create IPM->inbox
    O_IFolder *inbox;
    result = IPMroot->CreateFolder(FOLDER_GENERIC,"Inbox",
                        "",NULL,OPEN_IF_EXISTS,(LPMAPIFOLDER *)&inbox);

    // Create IPM->outbox
    O_IFolder *outbox;
    result = IPMroot->CreateFolder(FOLDER_GENERIC,"Outbox",
                        "",NULL,OPEN_IF_EXISTS,(LPMAPIFOLDER *)&outbox);

    //PR_IPM_OUTBOX_ENTRYID
	defaultProps[propCount].ulPropTag = PR_IPM_OUTBOX_ENTRYID;
	defaultProps[propCount].Value.bin.lpb = (LPBYTE)(outbox->entryID);
	defaultProps[propCount++].Value.bin.cb  = sizeof(O_ENTRYID);

    // Create IPM->WasteBasket
    O_IFolder *waste;
    result = IPMroot->CreateFolder(FOLDER_GENERIC,"Waste Basket",
                        "",NULL,OPEN_IF_EXISTS,(LPMAPIFOLDER *)&waste);

    //PR_IPM_WASTEBASKET_ENTRYID
	defaultProps[propCount].ulPropTag = PR_IPM_WASTEBASKET_ENTRYID;
	defaultProps[propCount].Value.bin.lpb = (LPBYTE)(waste->entryID);
	defaultProps[propCount++].Value.bin.cb  = sizeof(O_ENTRYID);

    // Create IPM->Sent
    O_IFolder *sent;
    result = IPMroot->CreateFolder(FOLDER_GENERIC,"Sent",
                        "",NULL,OPEN_IF_EXISTS,(LPMAPIFOLDER *)&sent);

    //PR_IPM_SENTMAIL_ENTRYID
	defaultProps[propCount].ulPropTag = PR_IPM_SENTMAIL_ENTRYID;
	defaultProps[propCount].Value.bin.lpb = (LPBYTE)(sent->entryID);
	defaultProps[propCount++].Value.bin.cb  = sizeof(O_ENTRYID);
    
    // Create IPM->views
    O_IFolder *views;
    result = rootFolder->CreateFolder(FOLDER_GENERIC,"Views",
                        "",NULL,OPEN_IF_EXISTS,(LPMAPIFOLDER *)&views);

    //PR_VIEWS_ENTRYID
	defaultProps[propCount].ulPropTag = PR_VIEWS_ENTRYID;
	defaultProps[propCount].Value.bin.lpb = (LPBYTE)(views->entryID);
	defaultProps[propCount++].Value.bin.cb  = sizeof(O_ENTRYID);
    
    // Create IPM->commonViews
    O_IFolder *commonViews;
    result = rootFolder->CreateFolder(FOLDER_GENERIC,"Common Views",
                        "",NULL,OPEN_IF_EXISTS,(LPMAPIFOLDER *)&commonViews);

    //PR_COMMON_VIEWS_ENTRYID
	defaultProps[propCount].ulPropTag = PR_COMMON_VIEWS_ENTRYID;
	defaultProps[propCount].Value.bin.lpb = (LPBYTE)(commonViews->entryID);
	defaultProps[propCount++].Value.bin.cb  = sizeof(O_ENTRYID);

    // Create IPM->finder
    O_IFolder *finder;
    result = rootFolder->CreateFolder(FOLDER_GENERIC,"Finder",
                        "",NULL,OPEN_IF_EXISTS,(LPMAPIFOLDER *)&finder);

    //PR_FINDER_ENTRYID
	defaultProps[propCount].ulPropTag = PR_FINDER_ENTRYID;
	defaultProps[propCount].Value.bin.lpb = (LPBYTE)(finder->entryID);
	defaultProps[propCount++].Value.bin.cb  = sizeof(O_ENTRYID);

    //IPMroot->Dump();

    //PR_OBJECT_TYPE
    //The primary interface of this object
    defaultProps[propCount].ulPropTag = PR_OBJECT_TYPE;
    defaultProps[propCount++].Value.l   =   MAPI_STORE;
 
    //PR_VALID_FOLDER_MASK
    //Indicates the validity of entry IDs in this store
    defaultProps[propCount].ulPropTag  = PR_VALID_FOLDER_MASK;
    defaultProps[propCount++].Value.l  =      FOLDER_IPM_SUBTREE_VALID
        | FOLDER_FINDER_VALID | FOLDER_VIEWS_VALID | FOLDER_COMMON_VIEWS_VALID
        | FOLDER_IPM_INBOX_VALID | FOLDER_IPM_OUTBOX_VALID | FOLDER_IPM_WASTEBASKET_VALID
        | FOLDER_IPM_SENTMAIL_VALID;

    //PR_RECORD_KEY
    defaultProps[propCount].ulPropTag  = PR_RECORD_KEY;
    defaultProps[propCount].Value.bin.lpb = (LPBYTE)&(entryID->instanceGuid);
	defaultProps[propCount++].Value.bin.cb  = sizeof(MAPIUID);

    defaultProps[propCount].ulPropTag  = PR_ACCESS_LEVEL;
    defaultProps[propCount++].Value.l = MAPI_MODIFY;

    defaultProps[propCount].ulPropTag  = PR_PST_PATH;
    defaultProps[propCount++].Value.lpszA = databaseFilename;

    // FIXME:  Should those be set on the root folder as well??
    //         especially a lot of the PR_IPM_* properties??
    result = O_IProp::_IntSetProps(propCount, (LPSPropValue)&defaultProps, NULL );

    // Outgoing queue table...
    propCount = 0;
    SizedSPropTagArray(17, ogqProps);

    ogqProps.aulPropTag[propCount++] = PR_INSTANCE_KEY;
    ogqProps.aulPropTag[propCount++] = PR_CLIENT_SUBMIT_TIME;
    ogqProps.aulPropTag[propCount++] = PR_DISPLAY_CC;
    ogqProps.aulPropTag[propCount++] = PR_ENTRYID;
    ogqProps.aulPropTag[propCount++] = PR_MESSAGE_SIZE;
    ogqProps.aulPropTag[propCount++] = PR_SENDER_NAME;
    ogqProps.aulPropTag[propCount++] = PR_SUBMIT_FLAGS;
    ogqProps.aulPropTag[propCount++] = PR_DISPLAY_BCC;
    ogqProps.aulPropTag[propCount++] = PR_DISPLAY_TO;
    ogqProps.aulPropTag[propCount++] = PR_MESSAGE_FLAGS;
    ogqProps.aulPropTag[propCount++] = PR_PRIORITY;
    ogqProps.aulPropTag[propCount++] = PR_SUBJECT;
    ogqProps.aulPropTag[propCount++] = PR_NULL;
    ogqProps.aulPropTag[propCount++] = PR_NULL;
    ogqProps.aulPropTag[propCount++] = PR_NULL;
    ogqProps.aulPropTag[propCount++] = PR_NULL;
    ogqProps.aulPropTag[propCount++] = PR_NULL;

    ogqProps.cValues = propCount;

    // Get the receive folder named property tag id
    tmpNameID.ulKind = MNID_STRING;
    tmpNameID.lpguid = (LPGUID)&otlkcon_mdb_provider_guid;
    tmpNameID.Kind.lpwstrName = OTLKCON_OUTGOINGQUEUE_NAME;
    result = GetIDsFromNames(1, &tmpNameIDs, MAPI_CREATE, &tmpPropTagArray);

    tmpPropTag = PROP_TAG(PT_OBJECT, PROP_ID(tmpPropTagArray->aulPropTag[0]));
    result = OpenProperty(tmpPropTag, &IID_IMAPITable, 0, 
                    MAPI_CREATE|MAPI_MODIFY, (LPUNKNOWN *)&outgoingQueueTable);
    if( result != S_OK )
    {
        OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO, OTLKCON_LOG_TARGET_ALL, 
            L"IMsgStore::Create() failed while opening outgoing queue table.");
        OTLKCON_DEBUGBREAK
        result = E_FAIL;
        goto create_fail;
    }
    result = outgoingQueueTable->Init( (LPSPropTagArray)&ogqProps );
    if( result != S_OK )
    {
        OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO, OTLKCON_LOG_TARGET_ALL, 
            L"IMsgStore::Create() failed while opening outgoing queue table.");
        OTLKCON_DEBUGBREAK
        result = E_FAIL;
        goto create_fail;
    }
    outgoingQueueTable->objName = memObj->_strdup("outgoingQueueTable");

    // Receive folder table
    propCount = 0;
    SizedSPropTagArray(7, recvfldProps);

    recvfldProps.aulPropTag[propCount++] = PR_INSTANCE_KEY;
    recvfldProps.aulPropTag[propCount++] = PR_ENTRYID;
    recvfldProps.aulPropTag[propCount++] = PR_MESSAGE_CLASS;
    recvfldProps.aulPropTag[propCount++] = PR_RECORD_KEY;
    recvfldProps.aulPropTag[propCount++] = PR_NULL;
    recvfldProps.aulPropTag[propCount++] = PR_NULL;
    recvfldProps.aulPropTag[propCount++] = PR_NULL;

    recvfldProps.cValues = propCount;

    // Get the receive folder named property tag id
    recvFld.ulKind = MNID_STRING;
    recvFld.lpguid = (LPGUID)&otlkcon_mdb_provider_guid;
    recvFld.Kind.lpwstrName = OTLKCON_RECEIVEFOLDERTABLE_NAME;
    result = GetIDsFromNames(1, &recvFlds, MAPI_CREATE, &recvFldPropTag);

    // 'Receive Folder Table' Property
    // Experimenting with keeping the receive folder table around...
    tmpPropTag = PROP_TAG(PT_OBJECT, PROP_ID(recvFldPropTag->aulPropTag[0]));
    result = OpenProperty(tmpPropTag, &IID_IMAPITable, 0, 
                    MAPI_CREATE|MAPI_MODIFY, (LPUNKNOWN *)&receiveFolderTable);
    if( result != S_OK )
    {
        OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO, OTLKCON_LOG_TARGET_ALL, 
            L"IMsgStore::Create() failed :  CreateTable() failed for receive folder table");
        OTLKCON_DEBUGBREAK
        result = E_FAIL;
        goto create_fail;
    }
    result = receiveFolderTable->Init( (LPSPropTagArray)&recvfldProps );
    if( result != S_OK )
    {
        OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO, OTLKCON_LOG_TARGET_ALL, 
            L"IMsgStore::Create() failed :  CreateTable() failed for receive folder table");
        OTLKCON_DEBUGBREAK
        result = E_FAIL;
        goto create_fail;
    }
    receiveFolderTable->objName = memObj->_strdup("receiveFolderTable");

    LPSRow fldRow = (LPSRow)memObj->malloc( sizeof(SRow) );
    LPSPropValue props = (LPSPropValue)memObj->malloc( propCount*sizeof(SPropValue) );

    propCount = 0;

    // FIXME: Should the instance key be the entryid or the instance
    //        or the instance/type Guid pair.  Using the entire entryid
    //        may cause issues in the future.
    props[propCount].ulPropTag = PR_INSTANCE_KEY;
    props[propCount].Value.bin.cb = sizeof(O_ENTRYID);
    props[propCount++].Value.bin.lpb = (LPBYTE)(inbox->entryID);

    props[propCount].ulPropTag = PR_ENTRYID;
    props[propCount].Value.bin.cb = sizeof(O_ENTRYID);
    props[propCount++].Value.bin.lpb = (LPBYTE)(inbox->entryID);

    props[propCount].ulPropTag = PR_MESSAGE_CLASS;
    props[propCount++].Value.lpszA = memObj->_strdup("IPM");

    props[propCount].ulPropTag = PR_RECORD_KEY;
    props[propCount].Value.bin.cb = sizeof(O_ENTRYID);
    props[propCount++].Value.bin.lpb = (LPBYTE)(inbox->entryID);

    LPSRow row   = (LPSRow)memObj->malloc( sizeof(SRow) );
    row->lpProps = props;
    row->cValues = propCount;

    result = receiveFolderTable->HrModifyRow(row);
    if( result != S_OK )
    {
        OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO, OTLKCON_LOG_TARGET_ALL, 
            L"IMsgStore::Create() failed adding a receive folder");
        OTLKCON_DEBUGBREAK
    }

    result = LoadForeignAppointmentFolders();
    
	//We are forcing notifications to be delivered
	//i.e. forcing updating hierarchy tables
	HrDispatchNotifications(0);

create_fail:
    OTLKCON_LEAVECRITICALSECTION(&objectCS, "OBJ> otlkcon_generic_msgstore::Create");
    OTLKCON_LEAVECRITICALSECTION(&otlkcon_sqlite_cs, "SQL> otlkcon_generic_msgstore::Create");
    
    return result;
}

HRESULT O_IMSPMsgStore::Destroy()
{
    HRESULT result = S_OK;

	if(NULL != m_lpParentProvider)
	{
		m_lpParentProvider->RemoveLoggedStore(this);
		lpMAPISup->StoreLogoffTransports(&m_ulCachedStoreLogOffFlags);
		lpMAPISup->Release();
		lpMAPISup = NULL;
	} // if    
	
	return result;
}

HRESULT O_IMSPMsgStore::Load(char *df, O_ENTRYID *eid, otlkcon_memory *m )
{
    HRESULT result = S_OK;
    LPSPropTagArray tmpPropTagArray;
    MAPINAMEID tmpNameID = {0};
    LPMAPINAMEID tmpNameIDs = &tmpNameID;
    LPSPropTagArray recvFldPropTag;
    MAPINAMEID recvFld = {0};
    LPMAPINAMEID recvFlds = &recvFld;
    int tmpPropTag;

    OTLKCON_ENTERCRITICALSECTION(&otlkcon_sqlite_cs, "SQL> otlkcon_generic_msgstore::Load");
    OTLKCON_ENTERCRITICALSECTION(&objectCS, "OBJ> otlkcon_generic_msgstore::Load");
    
    result = otlkcon_generic_msgstore::Load(df,eid,m);
    if( result != S_OK )
    {
        goto load_end;
    }

    // Get the receive folder named property tag id
    recvFld.ulKind = MNID_STRING;
    recvFld.lpguid = (LPGUID)&otlkcon_mdb_provider_guid;
    recvFld.Kind.lpwstrName = OTLKCON_RECEIVEFOLDERTABLE_NAME;
    result = GetIDsFromNames(1, &recvFlds, MAPI_CREATE, &recvFldPropTag);

    // Open the receive folder table from the correct named property
    tmpPropTag = PROP_TAG(PT_OBJECT, PROP_ID(recvFldPropTag->aulPropTag[0]));
    result = OpenProperty(tmpPropTag, &IID_IMAPITable, 0, 
                    MAPI_MODIFY, (LPUNKNOWN *)&receiveFolderTable);
    if( result != S_OK )
    {
        OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO, OTLKCON_LOG_TARGET_ALL, 
            L"IMsgStore::Load() failed loading a receive folder");
        OTLKCON_DEBUGBREAK
    }
    receiveFolderTable->objName = memObj->_strdup("receiveFolderTable");

    // Get the receive folder named property tag id
    tmpNameID.ulKind = MNID_STRING;
    tmpNameID.lpguid = (LPGUID)&otlkcon_mdb_provider_guid;
    tmpNameID.Kind.lpwstrName = OTLKCON_OUTGOINGQUEUE_NAME;
    result = GetIDsFromNames(1, &tmpNameIDs, MAPI_CREATE, &tmpPropTagArray);

    // Open the outgoing queue table from the correct named property
    tmpPropTag = PROP_TAG(PT_OBJECT, PROP_ID(tmpPropTagArray->aulPropTag[0]));
    result = OpenProperty(tmpPropTag, &IID_IMAPITable, 0, 
                    MAPI_MODIFY, (LPUNKNOWN *)&outgoingQueueTable);
    if( result != S_OK )
    {
        OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO, OTLKCON_LOG_TARGET_ALL, 
            L"IMsgStore::Load() failed loading outgoing queue");
        OTLKCON_DEBUGBREAK
    }
    outgoingQueueTable->objName = memObj->_strdup("outgoingQueueTable");

    //TODO: Load IPM Receive Folder?

    //TODO: Load "Top level folders" Map?

    // Load 'account_appointment_folder_mapping_table into 
    result = LoadForeignAppointmentFolders();

load_end:
    OTLKCON_LEAVECRITICALSECTION(&objectCS, "OBJ> otlkcon_generic_msgstore::Load");
    OTLKCON_LEAVECRITICALSECTION(&otlkcon_sqlite_cs, "SQL> otlkcon_generic_msgstore::Load");

    return result;
}

// TODO: We should store the "receive folder table" in a property
STDMETHODIMP O_IMSPMsgStore::GetReceiveFolderTable(
  ULONG ulFlags,
  LPMAPITABLE *lppTable  
)
{
    HRESULT result = S_OK;

    //Validate_IMsgStore_GetReceiveFolderTable(this,ulFlags,lppTable);

    OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO, OTLKCON_LOG_TARGET_ALL, 
        L"inside IMsgStore->GetReceiveFolderTable.\n");

    result = receiveFolderTable->HrGetView(0,0,0,lppTable);
    if( result != S_OK )
    {
        OTLKCON_DEBUGBREAK
        return E_FAIL;
    }

    return result;
}

STDMETHODIMP O_IMSPMsgStore::GetOutgoingQueue(
  ULONG ulFlags,
  LPMAPITABLE *lppTable  
)
{
  HRESULT result = S_OK;

  Validate_IMsgStore_GetOutgoingQueue(this,ulFlags,lppTable);

  OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO, OTLKCON_LOG_TARGET_ALL, 
      L"inside IMsgStore->GetOutgoingQueue.\n");

  result = outgoingQueueTable->HrGetView(0,0,0,lppTable);
  if( result != S_OK )
  {
      OTLKCON_DEBUGBREAK
      return E_FAIL;
  }

  return result;
}

STDMETHODIMP O_IMSPMsgStore::GetReceiveFolder(
  LPTSTR lpszMessageClass,
  ULONG ulFlags,
  ULONG *lpcbEntryID,
  LPENTRYID *lppEntryID,
  LPTSTR *lppszExplicitClass  
)
{
    HRESULT result = S_OK;
    char *messageClass;
    O_ENTRYID *tmpEntryID = NULL;
    SPropValue prop = {0};
    SRestriction rt = {0};
    SContentRestriction cr = {0};
    size_t i;

    LPSRowSet folderRow = NULL;
    //Validate_IMsgStore_GetReceiveFolder(this,lpszMessageClass,ulFlags,
      //          lpcbEntryID,lppEntryID,lppszExplicitClass);

    lastCalledFunctionID = OTLKCON_IMSGSTORE_GETRECEIVEFOLDER;
    strcpy_s(lastCalledFunctionName, OTLKCON_GETLASTERROR_FUNCTION_NAME_SIZE+1,
        OTLKCON_IMSGSTORE_GETRECEIVEFOLDER_STR);
    SetLastError(S_OK, "");
        
    OTLKCON_ENTERCRITICALSECTION(&objectCS, "IMsgStore::GetReceiveFolder");

    OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO, OTLKCON_LOG_TARGET_ALL, 
        L"inside IMsgStore->GetReceiveFolder.\n");

    if( lpszMessageClass == NULL )
    {
        messageClass = memObj->_strdup( "IPM.Note" );
    }
    else
    {
        messageClass = memObj->_strdup( lpszMessageClass );
    }

    do
    {
        memset(&rt, 0, sizeof(SRestriction));
        memset(&prop, 0, sizeof(SPropValue));
        memset(&cr, 0, sizeof(SContentRestriction));

        rt.rt = RES_CONTENT;
       
        prop.ulPropTag  = PR_MESSAGE_CLASS;
        prop.Value.lpszA = messageClass;
        cr.lpProp = &prop;
        cr.ulFuzzyLevel = FL_FULLSTRING | FL_IGNORECASE;
        cr.ulPropTag = PR_MESSAGE_CLASS;
        rt.res.resContent = cr;

        result = receiveFolderTable->FindRow(&rt, BOOKMARK_BEGINNING, 0);
        switch(result)
        {
            case S_OK:
            case MAPI_W_POSITION_CHANGED:
                //Folder found, return entryID
                
                //result = receiveFolderTable->Dump(0);
                result = receiveFolderTable->QueryRows(1, 0, &folderRow);
                if( result != S_OK )
                {
                    OTLKCON_DEBUGBREAK;
                    goto getreceivefolder_end;
                }

                if( folderRow->cRows < 1 )
                {
                    // FIXME: Why would FindRow() succeed but the
                    //        following QueryRow() call fail?
                    OTLKCON_DEBUGBREAK;
                    result = E_FAIL;
					FreeProws(folderRow);
					folderRow = NULL;
                    goto getreceivefolder_end;
                }
				{
					LPSPropValue lpEntryID = PpropFindProp(
						folderRow->aRow[0].lpProps,
						folderRow->aRow[0].cValues, PR_ENTRYID);
					if(NULL != lpEntryID)
					{
						tmpEntryID = (O_ENTRYID *)memObj->bindup(lpEntryID->Value.bin.cb, lpEntryID->Value.bin.lpb);
						*lpcbEntryID = lpEntryID->Value.bin.cb;
					} // if
				}
				FreeProws(folderRow);
				folderRow = NULL;
                break;

            case MAPI_E_NOT_FOUND:
                //Folder was not found, truncate and try again
                for(i=strlen(messageClass)-1; i>0; i--)
                    if( messageClass[i] == '.' )
                        break;
                messageClass[i] = 0;
                break;

            default:
                OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO,  OTLKCON_LOG_TARGET_ALL,
                    L"GetReceiveFolder() : receiveFolderTable->FindRow returned %X",
                    result );
                OTLKCON_DEBUGBREAK
                break;
        }
    }
    while( strlen( messageClass ) > 1 && tmpEntryID==NULL );

    // Did we find the folder ?
    if( tmpEntryID == NULL )
    {
        // Try again, this time use "IPM"
        memset(&rt, 0, sizeof(SRestriction));
        memset(&prop, 0, sizeof(SPropValue));
        memset(&cr, 0, sizeof(SContentRestriction));

        rt.rt = RES_CONTENT;
       
        prop.ulPropTag  = PR_MESSAGE_CLASS;
        prop.Value.lpszA = memObj->_strdup("IPM");
        cr.lpProp = &prop;
        cr.ulFuzzyLevel = FL_FULLSTRING | FL_IGNORECASE;
        cr.ulPropTag = PR_MESSAGE_CLASS;
        rt.res.resContent = cr;

        result = receiveFolderTable->FindRow(&rt, BOOKMARK_BEGINNING, 0);
        switch(result)
        {
            case S_OK:
            case MAPI_W_POSITION_CHANGED:
                //Folder found, return entryID
                result = receiveFolderTable->QueryRows(1, 0, &folderRow);
				if( result != S_OK )
				{
					OTLKCON_DEBUGBREAK;
					goto getreceivefolder_end;
				}
                //result = receiveFolderTable->Dump(0);
				{
					LPSPropValue lpEntryID = PpropFindProp(
						folderRow->aRow[0].lpProps,
						folderRow->aRow[0].cValues, PR_ENTRYID);
					if(NULL != lpEntryID)
					{
						tmpEntryID = (O_ENTRYID *)memObj->bindup(lpEntryID->Value.bin.cb, lpEntryID->Value.bin.lpb);
						*lpcbEntryID = lpEntryID->Value.bin.cb;
					} // if
				}
				FreeProws(folderRow);
				folderRow = NULL;
                break;
            default:
                // We're asking for IPM, this should not happen
                OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO,  OTLKCON_LOG_TARGET_ALL,
                    L"GetReceiveFolder() : receiveFolderTable->FindRow returned %X",
                    result );
                OTLKCON_DEBUGBREAK
                break;
        }
        // Return an empty string
        if( lppszExplicitClass )
            *lppszExplicitClass = memObj->_strdup("");
        OTLKCON_DEBUGBREAK;
    }
    else
    {
        if( lppszExplicitClass )
            *lppszExplicitClass = memObj->_strdup( messageClass );
    }

    *lppEntryID = (LPENTRYID)tmpEntryID;

    OTLKCON_LEAVECRITICALSECTION(&objectCS, "IMsgStore::GetReceiveFolder");

getreceivefolder_end:
    return result;
}


HRESULT O_IMSPMsgStore::LoadForeignAppointmentFolders()
{
    HRESULT result = S_OK;
    const char **queryRemainder = NULL;
    sqlite3_stmt * queryStmt = NULL;
    char *queryStr;
    int sqlRes;
    char *tmpId, *tmpGuid;
    wchar_t *tmpIdW;
    MAPIUID tmpUid = {0};
    O_ENTRYID tmpEid = {0};
    O_IFolder *tmpFld, *defFld=NULL;
    ULONG objType;
    SizedSPropTagArray(1, calendarProp);
    ULONG cProps = 0;
    LPSPropValue propResult = 0;
    SPropValue defaultProps[10] = {0};
    O_ENTRYID *tmpEntryID;
    map<wstring,O_IFolder*>::iterator fldRes;

    // Get the account data
    LPPROFSECT lpProfSectObj = NULL;
    vector<otlkcon_account_details *> *accounts;
    ULONG profPropsCount = 0;
    SizedSPropTagArray(50, profTags);
    MAPIUID   *profServiceUid = NULL;
    LPPROFSECT lpProfileObj = NULL;
    map<string,string> tmpUidList;

    OTLKCON_ENTERCRITICALSECTION(&otlkcon_sqlite_cs, "SQL> otlkcon_generic_msgstore::LoadForeignAppointmentFolders");
    OTLKCON_ENTERCRITICALSECTION(&objectCS, "OBJ> otlkcon_generic_msgstore::LoadForeignAppointmentFolders");
    
    result = lpMAPISup->OpenProfileSection(NULL, MAPI_MODIFY, &lpProfileObj);
    if( result != S_OK )
    {
        // Call failed
        OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO,  OTLKCON_LOG_TARGET_ALL, 
            L"otlkcon_generic_msgstore::LoadForeignAppointFolders() : OpenProfileSection failed.\n" );
        result = E_FAIL;
        goto loadforeignappointmentfolders_end;
    }

    profTags.aulPropTag[profPropsCount++] = PR_SERVICE_UID;
	profTags.cValues = profPropsCount;

    result = lpProfileObj->GetProps((LPSPropTagArray)&profTags, 0, &profPropsCount, &propResult);
    switch( result )
	{
		case MAPI_W_ERRORS_RETURNED:
		case S_OK:			
            for( ULONG i=0; i<profPropsCount; i++ )
            {
                if( PROP_ID(propResult[i].ulPropTag) == PROP_ID(PR_SERVICE_UID) )
				{
					if( PROP_TYPE(propResult[i].ulPropTag) == PT_BINARY )
					{
						profServiceUid = (MAPIUID*)(propResult[i].Value.bin.lpb);
					}
                }
            }
    }

    result = lpMAPISup->OpenProfileSection(profServiceUid, MAPI_MODIFY, &lpProfSectObj);
    if( result != S_OK )
    {
        // Call failed
        OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO,  OTLKCON_LOG_TARGET_ALL, 
            L"IMsgStore::Create() : OpenProfileSection failed.\n" );
        result = E_FAIL;
        goto loadforeignappointmentfolders_end;
    }

    accounts = new vector<otlkcon_account_details *>;
    result = otlkcon_getAccountsData(accounts, lpProfSectObj );
    if( result != S_OK )
    {
        OTLKCON_DEBUGBREAK
        result = E_FAIL;
        goto loadforeignappointmentfolders_end;
    }

    tmpEid.entryIDType = MAPI_FOLDER;
    tmpEid.typeGuid = otlkcon_imapifolder_guid;
    tmpEid.storeInstanceGuid = entryID->instanceGuid;

    otlkcon_sqlite_helper * sqlObj = GetSqlObj();
	if(NULL == sqlObj)
	{
        OTLKCON_DEBUGBREAK
		result = E_FAIL;	
        goto loadforeignappointmentfolders_end;
	}

    tmpIdW = (wchar_t *)memObj->malloc( (MAX_PATH+1)*sizeof(wchar_t) );
    sqlRes = sqlObj->buildQuery( &queryStr,
                    "SELECT  * FROM account_appointment_folder_mapping_table" );
    sqlite3_prepare( sqlObj->databaseHandle, queryStr, -1, 
                                &queryStmt, queryRemainder);

    while( (sqlRes = sqlite3_step(queryStmt)) == SQLITE_ROW )
    {
        tmpId = (char *)sqlite3_column_text(queryStmt, 0);
        tmpGuid = (char *)sqlite3_column_text(queryStmt, 1);
        tmpUidList.insert( pair<string,string>(string(tmpId),string(tmpGuid)) );
    }

    sqlRes = sqlite3_finalize(queryStmt);
    if ( sqlRes != SQLITE_OK )
    {
        OTLKCON_DEBUGBREAK;
    }

    for( map<string,string>::iterator i=tmpUidList.begin();
        i!=tmpUidList.end(); ++i)
    {
        result = String2MAPIUID(&tmpUid, (*i).second.c_str());

        string tmpId3 = (*i).first.c_str();

        tmpEid.instanceGuid = tmpUid;
        tmpEid.primarySupportedInterface = OTLKCON_IMAPIFOLDER_CLASS;
        tmpEid.typeID = MAPI_FOLDER;

        result = OpenEntry(sizeof(O_ENTRYID), (LPENTRYID)&tmpEid, NULL, 0,
                                        &objType, (LPUNKNOWN *)&tmpFld );
        if( result != S_OK )
        {
            OTLKCON_DEBUGBREAK;
        }

        _snwprintf_s( tmpIdW, MAX_PATH+1, MAX_PATH, L"%S", (*i).first.c_str());
        foreignAppointmentFolders.insert( 
                        pair<wstring,O_IFolder *>( wstring(tmpIdW), tmpFld ) );
    }

    // Setup 0x36D0 folder if it does not exist
    calendarProp.cValues = 1;
    calendarProp.aulPropTag[0] = PROP_TAG(PT_BINARY, 0x36D0);
    result = O_IProp::_IntGetProps( (LPSPropTagArray)&calendarProp, 
        0, &cProps, &propResult );
    switch( result )
	{
		case MAPI_W_ERRORS_RETURNED:
		case S_OK:			
            if( PROP_ID(propResult[0].ulPropTag) == 0x36D0 )
            {
                // FIXME:  We should check for specific errors
                if( PROP_TYPE(propResult[0].ulPropTag) == PT_BINARY )
                {
                    tmpEntryID = (O_ENTRYID*)(propResult[0].Value.bin.lpb);
                    result =  OpenEntry( sizeof(O_ENTRYID), (LPENTRYID)tmpEntryID, 
                            NULL, 0, &objType, (LPUNKNOWN *)&defFld );
                    if( result != S_OK )
                    {
                        // We can find the root folder
                        OTLKCON_DEBUGBREAK;
                        result = E_FAIL;
                        goto loadforeignappointmentfolders_end;
                    }
                }  
            }

			break;

		default:
			//Error condition
            OTLKCON_DEBUGBREAK;
            result = E_FAIL;
            goto loadforeignappointmentfolders_end;
    }

    // Get IPM Root folder
    O_IFolder *rtFldr = NULL;  
    calendarProp.cValues = 1;
    calendarProp.aulPropTag[0] = PR_IPM_SUBTREE_ENTRYID;
    result = _IntGetProps( (LPSPropTagArray)&calendarProp, 
        0, &cProps, &propResult );

    switch( result )
	{
		case MAPI_W_ERRORS_RETURNED:
		case S_OK:			
            if( PROP_ID(propResult[0].ulPropTag) == PROP_ID(PR_IPM_SUBTREE_ENTRYID) )
            {
                // FIXME:  We should check for specific errors
                if( PROP_TYPE(propResult[0].ulPropTag) == PT_BINARY )
                {
                    tmpEntryID = (O_ENTRYID*)(propResult[0].Value.bin.lpb);
                    result =  OpenEntry( sizeof(O_ENTRYID), (LPENTRYID)tmpEntryID, 
                            NULL, 0, &objType, (LPUNKNOWN *)&rtFldr );
                    if( result != S_OK )
                    {
                        // We can find the IPM root folder
                        OTLKCON_DEBUGBREAK;
                        result = E_FAIL;
                        goto loadforeignappointmentfolders_end;
                    }
                }  
            }

			break;

		default:
			//Error condition
            OTLKCON_DEBUGBREAK;
            result = E_FAIL;
            goto loadforeignappointmentfolders_end;
    }

    if( defFld == NULL )
    {
        // Create the default calendar folder
        result = rtFldr->CreateFolder(FOLDER_GENERIC,"Calendar",
                        "",NULL,OPEN_IF_EXISTS,(LPMAPIFOLDER *)&defFld);
        if( result != S_OK )
        {
            OTLKCON_DEBUGBREAK
            result = E_FAIL;
            goto loadforeignappointmentfolders_end;
        }
            
        defaultProps[0].ulPropTag = PR_CONTAINER_CLASS;
        defaultProps[0].Value.lpszA = "IPF.Appointment";
        result = defFld->SetProps(1, (LPSPropValue)&defaultProps, NULL);

        //Calendar entryID
	    defaultProps[0].ulPropTag = PROP_TAG(PT_BINARY, 0x36D0);
	    defaultProps[0].Value.bin.lpb = (LPBYTE)(defFld->entryID);
	    defaultProps[0].Value.bin.cb  = sizeof(O_ENTRYID);
        result = SetProps(1, (LPSPropValue)&defaultProps, NULL);
    }

    // Mark it as a remote folder
    defFld->isForeignView = TRUE;

    // Do we have any accounts without a folder registered?
    char *tmpIDA = (char *)memObj->malloc( MAX_PATH+1 );
    for( vector<otlkcon_account_details *>::iterator i=accounts->begin();
                i != accounts->end(); ++i )
    {
        if( (*i)->id.empty() )
        {
            continue;
        }

        // Find the account in the list
        fldRes = foreignAppointmentFolders.find( (*i)->id );
        if( fldRes == foreignAppointmentFolders.end() )
        {
            // This account is not registered to a folder.
            // Get it one.
            
            if( (*i)->defaultAccount == TRUE )
            {
                // Set this account with the 0x36D0 folder
                tmpFld = defFld;
            }
            else
            {
                // Create a new folder
                _snprintf_s(tmpIDA,MAX_PATH,MAX_PATH,"%S", (*i)->id.c_str() );
                result = rtFldr->CreateFolder(FOLDER_GENERIC, tmpIDA,
                            "",NULL,OPEN_IF_EXISTS,(LPMAPIFOLDER *)&tmpFld);

                defaultProps[0].ulPropTag = PR_CONTAINER_CLASS;
                defaultProps[0].Value.lpszA = "IPF.Appointment";
                result = tmpFld->SetProps(1, (LPSPropValue)&defaultProps, NULL);

                tmpFld->isForeignView = TRUE;
            }

            result = RegisterAppointmentFolder( (*i)->id, tmpFld );
        }

    }

loadforeignappointmentfolders_end:
    OTLKCON_LEAVECRITICALSECTION(&objectCS, "OBJ> otlkcon_generic_msgstore::LoadForeignAppointmentFolders");
    OTLKCON_LEAVECRITICALSECTION(&otlkcon_sqlite_cs, "SQL> otlkcon_generic_msgstore::LoadForeignAppointmentFolders");
    
    return result;
}

HRESULT O_IMSPMsgStore::DeleteAppointments( wstring accountId, map<wstring,wstring> namePairs )
{
    HRESULT result = S_OK;


    // The hash value in the pair is optional.  Leave empty to
    // signify "delete any version found".

    return result;
}

HRESULT O_IMSPMsgStore::OnAppointmentNameReq( otlkcon_event_struct *ev )
{
    HRESULT result = S_OK;
    wchar_t *tmpStr = NULL, *tmpId, *tmpHash;
    char *tmpIDStrA = NULL;
    char *tmpHashStrA = NULL;
    wstring idStr;
    O_IFolder *apptFld = NULL;
    otlkcon_event_struct *event = NULL;

    vector<wstring> tmpResList;
    ULONG tmpResListCount = 0;

    SRestriction rt = {0};
    SRestriction idAndHashRT = {0};
    SRestriction idAndHashRTValues[2] = {0};

//    SContentRestriction fIDCR = {0};
//    SContentRestriction fHashCR = {0};
	SPropertyRestriction fIDCR = {0};
	SPropertyRestriction fHashCR = {0};
    SAndRestriction andCR = {0};
    SPropValue fIDProp = {0};
    SPropValue fHashProp = {0};

    OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO,  
        OTLKCON_LOG_TARGET_ALL, 
        L"O_IMsgStore::OnAppointmentNameReq() called\n", className);

    if( ev == NULL )
    {
        OTLKCON_DEBUGBREAK
        return E_FAIL;
    }

    tmpStr = (wchar_t *)&(ev->data);
    idStr = tmpStr;

    if( idStr.length() < 1 )
    {
        OTLKCON_DEBUGBREAK;
        return E_FAIL;
    }

    OTLKCON_ENTERCRITICALSECTION(&otlkcon_sqlite_cs, 
                    "SQL> otlkcon_generic_msgstore::OnAppointmentNameReq");
    OTLKCON_ENTERCRITICALSECTION(&objectCS, "OBJ> otlkcon_generic_msgstore::OnAppointmentNameReq");
    
    map<wstring,O_IFolder*>::iterator apptFldI 
        = foreignAppointmentFolders.find( idStr );
    if( apptFldI == foreignAppointmentFolders.end() )
    {
        // No appointment folder with this id exists
        OTLKCON_DEBUGBREAK
        result = E_FAIL;
        goto onappointmentnamereq_end;
    }

    apptFld = apptFldI->second;

    fIDProp.ulPropTag  = PR_OTLKCON_FOREIGN_ID;
    fIDCR.lpProp = &fIDProp;
    fIDCR.ulPropTag = PR_OTLKCON_FOREIGN_ID;
    fIDCR.relop = RELOP_EQ;

    fHashProp.ulPropTag  = PR_OTLKCON_FOREIGN_HASH;
    fHashCR.lpProp = &fHashProp;
    fHashCR.ulPropTag = PR_OTLKCON_FOREIGN_HASH;
    fHashCR.relop = RELOP_EQ;

    rt.rt = RES_PROPERTY;
    rt.res.resProperty = fIDCR;

	idAndHashRTValues[0].rt = RES_PROPERTY;
    idAndHashRTValues[0].res.resProperty = fIDCR;
    idAndHashRTValues[1].res.resProperty = fHashCR;
	idAndHashRTValues[1].rt = RES_PROPERTY;

    andCR.cRes = 2;
    andCR.lpRes = (LPSRestriction)&idAndHashRTValues;

    idAndHashRT.rt = RES_AND;
    idAndHashRT.res.resAnd = andCR;

    tmpIDStrA = (char *)memObj->malloc( MAX_PATH+1 );
    tmpHashStrA = (char *)memObj->malloc( MAX_PATH+1 );

    // FIXME: This loop needs to be optimized.  A lot of time being
    //        used by the FindRow() call especially.
    for( tmpStr += wcslen(tmpStr)+1; wcslen(tmpStr); tmpStr += wcslen(tmpStr)+1 )
    {
        tmpId = tmpStr;
        tmpStr += wcslen(tmpStr)+1;
        tmpHash = tmpStr;
        _snprintf_s(tmpIDStrA, MAX_PATH, MAX_PATH, "%S", tmpId);

        // Search the contents table for right message    
        fIDProp.Value.lpszA = tmpIDStrA;
//        result = apptFld->contentsTable->Dump(0);
        result = apptFld->contentsTable->FindRow(&rt, BOOKMARK_BEGINNING, 0);
        switch(result)
        {
            case S_OK:
            case MAPI_W_POSITION_CHANGED:
                //Message found, but does it have the correct hash?
                _snprintf_s(tmpHashStrA, MAX_PATH, MAX_PATH, "%S", tmpHash);
                fHashProp.Value.lpszA = tmpHashStrA;
                result = apptFld->contentsTable->FindRow(&idAndHashRT, BOOKMARK_BEGINNING, 0);
                switch(result)
                {
                    case S_OK:
                    case MAPI_W_POSITION_CHANGED:
                        // There hasn't been any change.
                        ;
#if !defined(OTLKCON_ALWAYS_DOWNLOAD_APPTS) || OTLKCON_ALWAYS_DOWNLOAD_APPTS == 0 
                        break;
#endif

                    case MAPI_E_NOT_FOUND:
                        // Hash differs.  Add to request list
                        tmpResList.push_back( wstring(tmpId) );
                        tmpResListCount += (ULONG)wcslen(tmpId) + 1;
                        break;

                    default:
                        OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO,  OTLKCON_LOG_TARGET_ALL,
                            L"otlkcon_generic_msgstore::OnAppointmentNameReq() : contentsTable->FindRow returned %X",
                            result );
                        OTLKCON_DEBUGBREAK;
                        break;
                }
                break;

            case MAPI_E_NOT_FOUND:
                // Message was not found.
                // New item.  Add to request list
                tmpResList.push_back( wstring(tmpId) );
                tmpResListCount += (ULONG)wcslen(tmpId) + 1;
                break;

            default:
                OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO,  OTLKCON_LOG_TARGET_ALL,
                    L"otlkcon_generic_msgstore::OnAppointmentNameReq() : contentsTable->FindRow returned %X",
                    result );
                OTLKCON_DEBUGBREAK;
                break;
        }
    }

    event 
        = (otlkcon_event_struct *)memObj->pMalloc(sizeof(otlkcon_event_struct)
                    + tmpResListCount*sizeof(wchar_t) );
    memcpy(&(event->senderEntryID), entryID, sizeof(O_ENTRYID));
    memcpy(&(event->entryID), &(ev->senderEntryID), sizeof(O_ENTRYID));
    event->cbData            = tmpResListCount*sizeof(wchar_t);
    event->senderHWND = otlkcon_eventHwnd;
    
    // Flatten the vector
    tmpStr = (wchar_t *)&(event->data);
    for( vector<wstring>::iterator i=tmpResList.begin();
        i!=tmpResList.end(); ++i )
    {
        wcscpy_s( tmpStr, tmpResListCount, (*i).c_str() );
        tmpResListCount -= (ULONG)wcslen(tmpStr)+1;
        tmpStr += wcslen(tmpStr);
        tmpStr++;
    }

    event->eventType = OTLKCON_EVENT_APPOINTMENT_NAME_RES;
    result = otlkcon_event_sendmessage( otlkcon_eventHwnd, otlkcon_eventHwnd, event );
    memObj->pFree( event );

onappointmentnamereq_end:
    OTLKCON_LEAVECRITICALSECTION(&objectCS, "OBJ> otlkcon_generic_msgstore::OnAppointmentNameReq");
    OTLKCON_LEAVECRITICALSECTION(&otlkcon_sqlite_cs, "SQL> otlkcon_generic_msgstore::OnAppointmentNameReq");
    
    return result;
}

HRESULT O_IMSPMsgStore::RegisterAppointmentFolder( wstring accountId,
                                               O_IFolder *fld )
{
    HRESULT result = S_OK;
    char *tmpGuid;
    SPropValue defaultProps[1] = {0};
    char *accountIdA;

	OTLKCON_ENTERCRITICALSECTION(&otlkcon_sqlite_cs, "SQL> otlkcon_generic_msgstore::RegisterAppointmentFolder");
    OTLKCON_ENTERCRITICALSECTION(&objectCS, "OBJ> otlkcon_generic_msgstore::RegisterAppointmentFolder");

	otlkcon_sqlite_helper * sqlObj = GetSqlObj();
	if(NULL == sqlObj)
	{
		result = E_FAIL;
        goto registerappointmentfolder_fail;
	} // if
    
	tmpGuid = (char *)memObj->malloc(50);
    MAPIUID2String(fld->entryID->instanceGuid, &tmpGuid);

    accountIdA = (char*)memObj->pMalloc( (ULONG)accountId.length()+1 );
    _snprintf_s(accountIdA, accountId.length()+1, accountId.length(),
                        "%S", accountId.c_str() );

    result = sqlObj->sqlCommand("INSERT OR REPLACE INTO \
                                account_appointment_folder_mapping_table \
                                VALUES ( '%s', '%s' )", accountIdA, tmpGuid);

    foreignAppointmentFolders.insert( pair<wstring,O_IFolder*>(accountId,fld) );

    // PR_OTLKCON_FOREIGN_ID
    defaultProps[0].ulPropTag   = PR_OTLKCON_FOREIGN_ID;
    defaultProps[0].Value.lpszA = accountIdA;

    result = fld->_IntSetProps(1,(LPSPropValue)&defaultProps,NULL);

    memObj->free( tmpGuid );

//registerappointmentfolder_end:
    OTLKCON_LEAVECRITICALSECTION(&objectCS, "OBJ> otlkcon_generic_msgstore::RegisterAppointmentFolder");
    OTLKCON_LEAVECRITICALSECTION(&otlkcon_sqlite_cs, "SQL> otlkcon_generic_msgstore::RegisterAppointmentFolder");

    return result;

registerappointmentfolder_fail:
    OTLKCON_LEAVECRITICALSECTION(&objectCS, "OBJ> otlkcon_generic_msgstore::RegisterAppointmentFolder");
    OTLKCON_LEAVECRITICALSECTION(&otlkcon_sqlite_cs, "SQL> otlkcon_generic_msgstore::RegisterAppointmentFolder");

    return result;
}

HRESULT O_IMSPMsgStore::OnSyncAppointmentComponents( otlkcon_event_struct *ev )
{
    HRESULT result = S_OK;
    O_IFolder *fld  = NULL;
    O_IMessage *msg = NULL;
    O_ENTRYID *tmpEid = NULL;
    ULONG objType;
    map<wstring,O_IFolder*>::iterator msgRes;
    otlkcon_calendar_component_array *comps;
    otlkcon_calendar_component *currComp;
    wchar_t *idStr = NULL;
    char *tmpIDStrA, *tmpHashStrA;
    SRestriction rt = {0};
    SPropertyRestriction fIDCR = {0};
	//SContentRestriction fIDCR = {0};
    SPropValue fIDProp = {0};
    SPropValue prop[2] = {0};
    LPSRowSet folderRow = NULL;
    O_ENTRYID *tmpEntryID;


    OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO,  
        OTLKCON_LOG_TARGET_ALL, 
        L"O_IMsgStore::OnSyncAppointmentComponents() called\n", className);

    if( ev == NULL )
    {
        OTLKCON_DEBUGBREAK
        return E_FAIL;
    }

    idStr = (wchar_t *)&(ev->data);
    comps 
        = (otlkcon_calendar_component_array *)(idStr+wcslen(idStr)+1);
    
    {
		OTLKCON_ENTERCRITICALSECTION(&objectCS, "OBJ> otlkcon_generic_msgstore::OnSyncAppointmentComponents");
		msgRes = foreignAppointmentFolders.find( idStr );
		//OTLKCON_LEAVECRITICALSECTION(&objectCS, "OBJ> otlkcon_generic_msgstore::OnSyncAppointmentComponents");
	}
    
	if( msgRes != foreignAppointmentFolders.end() )
    {
        fld = msgRes->second;
    }
    else
    {
        // Account not found
        OTLKCON_DEBUGBREAK;
        result = E_FAIL;
        goto onsyncappointmentcomponents_end;
    }

    fIDProp.ulPropTag  = PR_OTLKCON_FOREIGN_ID;
    fIDCR.lpProp       = &fIDProp;
    fIDCR.ulPropTag    = PR_OTLKCON_FOREIGN_ID;
    fIDCR.relop = RELOP_EQ;

	rt.rt = RES_PROPERTY;
    rt.res.resProperty = fIDCR;

    currComp = (otlkcon_calendar_component *)&(comps->bytes[0]);
    tmpIDStrA = (char *)memObj->malloc( MAX_PATH+1 );
    tmpHashStrA = (char *)memObj->malloc( MAX_PATH+1 );
    for( ULONG i = 0; i < comps->count; ++i )
    {
		OTLKCON_ENTERCRITICALSECTION(&otlkcon_sqlite_cs, "SQL> otlkcon_generic_msgstore::OnSyncAppointmentComponents");
        _snprintf_s(tmpIDStrA, MAX_PATH, MAX_PATH, "%S", currComp->name);
        fIDProp.Value.lpszA = tmpIDStrA;

//        result = fld->contentsTable->Dump(0);
        result = fld->contentsTable->FindRow(&rt, BOOKMARK_BEGINNING, 0);
        switch(result)
        {
            case S_OK:
            case MAPI_W_POSITION_CHANGED:
				// Modify event
                result = fld->contentsTable->QueryRows(1, 0, &folderRow);
                if( result != S_OK )
                {
                    OTLKCON_DEBUGBREAK;
                    goto onsyncappointmentcomponents_end;
                }

                if( folderRow->cRows < 1 )
                {
                    OTLKCON_DEBUGBREAK;
                    result = E_FAIL;
                    goto onsyncappointmentcomponents_end;
                }
          
				LPSPropValue lpEntryID;
				lpEntryID = PpropFindProp(folderRow->aRow[0].lpProps, folderRow->aRow[0].cValues, PR_ENTRYID);
				if(NULL != lpEntryID)
				{
					tmpEntryID = (O_ENTRYID *)lpEntryID->Value.bin.lpb;
					//*lpcbEntryID = folderRow->aRow[0].lpProps->Value.bin.cb;

					result =  OpenEntry( sizeof(O_ENTRYID), (LPENTRYID)tmpEntryID, 
						NULL, 0, &objType, (LPUNKNOWN *)&msg );
					if( result != S_OK )
					{
						OTLKCON_DEBUGBREAK;
						result = E_FAIL;
						goto onsyncappointmentcomponents_end;
					}
					//Henry
					//So far doing nothing
//					prop[0].ulPropTag = PR_OTLKCON_FOREIGN_HASH;
//					_snprintf(tmpHashStrA, MAX_PATH, "%S", currComp->hash);
//					prop[0].Value.lpszA = tmpHashStrA;
//					result = msg->_IntSetProps(1, (LPSPropValue)&prop, NULL);
//					result = msg->ICalImport( (wchar_t *)&(currComp->data.bytes) );
				} // if
				else
				{
					//EntryID is not found in the contents table
					OTLKCON_DEBUGBREAK;
					result = E_FAIL;
					goto onsyncappointmentcomponents_end;
				} // else
                break;

            case MAPI_E_NOT_FOUND:
                // Message was not found.  New event
                result = fld->CreateMessage(NULL,0,(LPMESSAGE *)&msg);
                if( result != S_OK )
                {
                    OTLKCON_DEBUGBREAK;
                    result = E_FAIL;
                    goto onsyncappointmentcomponents_end;
                }

                prop[0].ulPropTag = PR_OTLKCON_FOREIGN_ID;
                prop[0].Value.lpszA = tmpIDStrA;
                prop[1].ulPropTag = PR_OTLKCON_FOREIGN_HASH;
                _snprintf_s(tmpHashStrA, MAX_PATH, MAX_PATH, "%S", currComp->hash);
                prop[1].Value.lpszA = tmpHashStrA;
                result = msg->_IntSetProps(2, (LPSPropValue)&prop, NULL);

				result = msg->BeginDownloading();
				
				result = msg->ICalImportMIMEDIR((wchar_t *)&(currComp->data.bytes), fld);
				
				result = msg->CompleteDownloading();
//                result = msg->ICalImport( (wchar_t *)&(currComp->data.bytes) );
                break;

            default:
                OTLKCON_LOG_PRINTF(OTLKCON_LOG_LEVEL_INFO,  OTLKCON_LOG_TARGET_ALL,
                    L"otlkcon_generic_msgstore::OnSyncAppointmentComponents() : contentsTable->FindRow returned %X",
                    result );
                OTLKCON_DEBUGBREAK;
                break;
        }

        currComp = (otlkcon_calendar_component *)((char *)currComp+currComp->dataSize+sizeof(otlkcon_calendar_component));
    }

onsyncappointmentcomponents_end:
	if(NULL != folderRow)
	{
		FreeProws(folderRow);
		folderRow = NULL;
	} // if
//We don't need that 
//because LeaveCriticalSection is called from within destructor of CSWrapper
//destructor will be called even if exception occurred
//    OTLKCON_LEAVECRITICALSECTION(&objectCS, "OBJ> otlkcon_generic_msgstore::OnSyncAppointmentComponents");
//	OTLKCON_LEAVECRITICALSECTION(&otlkcon_sqlite_cs, "SQL> otlkcon_generic_msgstore::OnSyncAppointmentComponents");
    return result;
}

HRESULT O_IMSPMsgStore::ProcessEvent( otlkcon_event_struct *ev )
{
    HRESULT result = S_OK;

    switch( ev->eventType )
    {
        case OTLKCON_EVENT_APPOINTMENT_NAME_REQ:
            result = OnAppointmentNameReq( ev );
            break;

        case OTLKCON_EVENT_SYNC_APPOINTMENT_COMPS:
            result = OnSyncAppointmentComponents( ev );
            break;

        case OTLKCON_EVENT_RELOAD_CONFIG:
            result = OnReloadConfig( ev );
            break;

        default:
            OTLKCON_DEBUGBREAK;
            break;
    }

    return result;
}